<style lang="scss" scoped>
.box {
  height: 200vh;
  overflow: scroll;
}
</style>

<template>
<div class="box" ref="boxRef" @scroll="throttledFn">
  <div v-for="item in 1000" :key="item">{{ item }}</div>
</div>
</template>

<script setup>
import { ref } from 'vue'
/**
 * 创建一个节流函数，在给定的时间间隔内最多执行一次目标函数
 * @param {Function} func - 需要节流的目标函数
 * @param {Number} interval - 节流间隔时间(毫秒)
 * @param {Object} [options] - 节流的配置选项
 * @param {Boolean} [options.leading=true] - 是否第一次调用时立即执行目标函数
 * @param {Boolean} [options.trailing=false] - 是否在冷却时间结束后追加一次执行目标函数
 * @param {function} [options.resultCallback] - 每次节流函数执行后的回调函数，用于处理目标函数的返回值
 * @returns {Function} - 返回包装后的节流函数
 * @author baiqiang
 * @example
 * // 创建一个节流函数，每 1000 毫秒最多执行一次
 * const throttledFn = throttle(() => {
 *   console.log('节流函数执行了')
 * }, 1000)
 * 
 * // 绑定事件
 * document.addEventListener('scroll', throttledFn)
 * 
 * // 调用 cancel 方法取消节流
 * thorttledFn.cancel()
*/
function throttle(func, interval, options = { leading: true, trailing: false, resultCallback }) {
  // 1.记录上一次开始时间
  const { leading, trailing, resultCallback } = options
  // 每次点击时，重置当前最新时间
  let lastTime = 0
  // 记录
  let timer = null
  function _throttle(...args) {
    // 获取“第一次”调用时间
    let nowTime = new Date().getTime()
    // 必须第一次执行(lastTime) 且 用户传参调节为首次不立即执行(leading)
    if (!lastTime && !leading) lastTime = nowTime
      // 获取冷却时间：节流间隔时间 - 第一次响应时间 + 发起响应时间
    const remainTime = interval - (nowTime - lastTime)
    // 达到冷却，可以执行响应函数
    if (remainTime <= 0) {
      // 如果节流间隔后存在正常响应函数，将“最后一次响应函数”的定时器清除
      if (timer) {
        clearTimeout(timer)
        timer = null
      }
      const result = func.apply(this, args)
      // 返回值
      if (resultCallback) resultCallback(result)
      // 记录函数上一次被成功调用的时间戳
      lastTime = nowTime
      return
    }
    // 节流最后一次也可以执行的判断逻辑
    if (trailing && !timer) {
      // 返回 timer,只有冷却归零后还未存在正常调用，timer 尚未清零才能调用，一旦调用则重置 timer
      timer = setTimeout(() => {
        timer = null
        lastTime = !leading ? 0 : new Date().getTime()
        const result = func.apply(this, args)
        // 返回值
        if (resultCallback) resultCallback(result)
      }, remainTime)
    }
  }
  // 取消方法
  _throttle.cancel = function () {
    if (timer) clearTimeout(timer)
    timer = null
    lastTime = 0
  }
  return _throttle
}

const throttledFn = throttle(() => {
  console.log('节流函数执行了')
}, 3000, {
  leading: false,
  trailing: true,
  resultCallback: (res) => {
    console.log('节流函数执行结果', res)
  }
})
</script>